﻿using System.Collections.Generic;
using UnityEngine;

namespace ProjectTW.Foliage
{
    public static class RenderingPipielineCore
    {
        public const float Instances_Offset_Multipler = 1.75f;
        public static Stack<Dictionary<int, RenderingQueueInstance>> QueueInstancesPool = new Stack<Dictionary<int, RenderingQueueInstance>>();
        public static Stack<UNFastList<RenderingQueueMeshInstanceSimulator>> FastListPool = new Stack<UNFastList<RenderingQueueMeshInstanceSimulator>>();

        private static MaterialPropertyBlock s_PropertyBlock;

        internal static MaterialPropertyBlock PropertyBlock
        {
            get
            {
                if (s_PropertyBlock == null)
                {
                    s_PropertyBlock = new MaterialPropertyBlock();
                    s_PropertyBlock.SetVectorArray(FoliageMeshManager.PropertyIdWorldposition, new Vector4[GPUInstancingCore.Max_Instancing_Amount]);
                }

                return s_PropertyBlock;
            }
        }

        private struct ThreadedRenderingQueueData
        {
            public RenderingQueueReceiver QueueReceiver;
            public Vector3 OriginalCameraPosition;

            public FoliageChunk[] TargetedManagerInstances;

            public ThreadedRenderingQueueData(RenderingQueueReceiver queueReceiver, Vector3 originalCameraPosition)
            {
                this.QueueReceiver = queueReceiver;
                this.OriginalCameraPosition = originalCameraPosition;
                this.TargetedManagerInstances = queueReceiver.ChunkNeighbors;
            }
        }

        /// <summary>
        /// 创建渲染队列
        /// </summary>
        /// <param name="receiver"></param>
        /// <param name="fetchedQueueInstances"></param>
        /// <param name="threaded"></param>
        public static void CreateRenderingQueue(RenderingQueueReceiver receiver, bool threaded = true)
        {
            Vector3 originalCameraPosition = receiver.Camera.transform.position;
            originalCameraPosition.x = Mathf.Floor(originalCameraPosition.x);
            originalCameraPosition.z = Mathf.Floor(originalCameraPosition.z);
            FoliageManager.WarmUpDensityMaps(receiver.ChunkNeighbors, true);
            ThreadedRenderingQueueData threadData = new ThreadedRenderingQueueData(receiver, originalCameraPosition);

            ThreadTask<ThreadedRenderingQueueData> task = new ThreadTask<ThreadedRenderingQueueData>((ThreadedRenderingQueueData data) =>
            {
                CreateRenderingQueueByThreaded(data.QueueReceiver, data.TargetedManagerInstances, data.OriginalCameraPosition);
            }, threadData);

            if (threaded)
            {
                ThreadManager.Instance.RunOnThread(task);
            }
            else
            {
                task.Invoke();
            }
        }

        /// <summary>
        /// 创建草队列的私有线程操作
        /// </summary>
        /// <param name="renderingQueueReceiver"></param>
        /// <param name="chunksBaseparam>
        /// <param name="originalCameraPosition"></param>
        /// <param name="fetchedQueueInstances"></param>
        private static void CreateRenderingQueueByThreaded(RenderingQueueReceiver renderingQueueReceiver, FoliageChunk[] chunks, Vector3 originalCameraPosition)
        {
            FoliageChunk currentChunk;
            FoliageInstanceObject currentInstance;
            RenderingQueue renderingQueue;
            Vector3 normalizedCameraPosition;
            renderingQueueReceiver.ThreadedColdRenderingCache.Clear();

            // 遍历循环处理所有Chunk
            for (int i = 0; i < chunks.Length; i++)
            {
                currentChunk = chunks[i];
                normalizedCameraPosition = originalCameraPosition;

                if (currentChunk == null || !currentChunk.IsFoliageInstanceAttached || !currentChunk.AttachedFoliageInstance.Enabled)
                {
                    continue;
                }

                currentInstance = currentChunk.GetOrCreateFoliageManagerInstance();
                // 生成渲染队列对象
                renderingQueue = new RenderingQueue(currentInstance, currentChunk, normalizedCameraPosition, GetFetchedInstance());

                if (renderingQueue.QueueInstanceNull)
                {
                    continue;
                }

                // 更新密度
                renderingQueue.UpdateDensities();
                renderingQueueReceiver.ThreadedColdRenderingCache.Add(renderingQueue);
            }

            renderingQueueReceiver.ThreadedWarmedRenderingCache = renderingQueueReceiver.ThreadedColdRenderingCache.ToArray();
        }

        public static void RenderQueue(RenderingQueueReceiver receiver, Camera camera)
        {
            receiver.InternalReusableCameraPlanes = FoliageStandaloneUtility.CalculateFrustumPlanes(camera, receiver.InternalReusableCameraPlanes);
            FoliageManager.WarmUpDensityMaps(receiver.ChunkNeighbors);

            // 渲染当前队列所有原型实例
            UpdateInternalSettings();

            RenderingQueue[] renderingQueues = receiver.RenderingQueue;
            if (renderingQueues == null)
            {
                return;
            }

            for (int i = 0; i < renderingQueues.Length; i++)
            {
                if (renderingQueues[i] == null)
                {
                    continue;
                }

                renderingQueues[i].Render(camera, receiver.InternalReusableCameraPlanes);
            }
        }

        private static void UpdateInternalSettings()
        {
            RenderingQueueMeshInstanceSimulator.SettingsIsPlaying = Application.isPlaying;
            RenderingQueueMeshInstanceSimulator.SettingsUseQualitySettingsShadows = FoliageManager.Instance.UseQualitySettingsShadowDistance;
            RenderingQueueMeshInstanceSimulator.SettingsShadowsDistance = FoliageManager.Instance.FoliageShadowDistance;
            RenderingQueueMeshInstanceSimulator.CullingDisalbed = UNSettings.Instance.FoliageDisableCulling;
        }

        private static Dictionary<int, RenderingQueueInstance> GetFetchedInstance()
        {
            FoliagePrototype prototype;
            Dictionary<int, RenderingQueueInstance> queueInstance = QueueInstancesPool.Count > 0 ? QueueInstancesPool.Pop() : null;

            if (queueInstance == null)
            {
                queueInstance = new Dictionary<int, RenderingQueueInstance>();

                for (int b = 0; b < FoliageDB.UnSortedPrototypes.Count; b++)
                {
                    prototype = FoliageDB.UnSortedPrototypes[b];

                    if (!prototype.Enabled)
                    {
                        continue;
                    }

                    queueInstance.Add(prototype.Id, null);
                }
            }
            else
            {
                for (int b = 0; b < FoliageDB.UnSortedPrototypes.Count; b++)
                {
                    prototype = FoliageDB.UnSortedPrototypes[b];

                    if (!prototype.Enabled)
                    {
                        continue;
                    }

                    queueInstance[prototype.Id] = null;
                }
            }

            return queueInstance;
        }

        /// <summary>
        /// Cleanup and recycle the queue instances of this simulator so it gets added to the pool.
        /// </summary>
        /// <param name="simulator"></param>
        internal static void CleanupQueueInstances(RenderingQueue[] renderingQueueStash)
        {
            List<FoliagePrototype> prototypes = FoliageDB.UnSortedPrototypes;
            RenderingQueue renderingQueue;
            RenderingQueueInstance renderingQueueInstance;
            int pID;

            for (int i = 0; i < renderingQueueStash.Length; i++)
            {
                renderingQueue = renderingQueueStash[i];
                QueueInstancesPool.Push(renderingQueue.RenderInstances);

                if (renderingQueue.RenderInstances != null)
                {
                    for (int prototypeIndex = 0; prototypeIndex < prototypes.Count; prototypeIndex++)
                    {
                        pID = prototypes[prototypeIndex].Id;

                        if (!renderingQueue.RenderInstances.ContainsKey(pID))
                        {
                            continue;
                        }

                        renderingQueueInstance = renderingQueue.RenderInstances[pID];

                        if (renderingQueueInstance == null)
                        {
                            continue;
                        }

                        renderingQueueInstance.SimulatedMeshInstances.Clear();
                        FastListPool.Push(renderingQueueInstance.SimulatedMeshInstances);
                        renderingQueueInstance.SimulatedMeshInstances = null;
                    }

                    renderingQueue.RenderInstances = null;
                }
            }
        }
    }
}
